﻿using System.Web.Security;
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Linq;
using System.Data.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using ShareB.Models;
using System.Web;

namespace ShareB.Providers
{
    public class ShareBMembershipProvider : MembershipProvider
	{
        private string connectionString;
        ShareBDataContext db = new ShareBDataContext();
        private int minRequiredPasswordLength;

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (name == null || name.Length == 0)
            {
                name = "MyMembershipProvider";
            }

            //Initialize the abstract base class.
            base.Initialize(name, config);


            //construção da connection string
            ConnectionStringSettings connSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
            if (connSettings == null || String.IsNullOrEmpty(connSettings.ConnectionString))
                throw new ProviderException("The connection string is null or empty.");
            connectionString = connSettings.ConnectionString;

            minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
        }

        //Validação de users
        public override bool ValidateUser(string username, string password)
        {
            try
            {
                User u = db.Users.Single(p => p.Username == username);
                return (u != null && u.Password.Equals(password) && !u.Disabled);           //TODO assim nao notifica se o user esta apenas bloqueado
            }
            catch (Exception)
            {
                return false;
            }
        }

        //metodo helper para atribuir valores de configuração
        private string GetConfigValue(string configValue, string defaultValue)
        {
            return (String.IsNullOrEmpty(configValue) ? defaultValue : configValue);
        }


        //Não Original do provider : registar utilizadores
        public MembershipUser CreateUser(string username, string password, string email, string firstName, string lastName, string address, byte[] photograph, string passwordQuestion,
    string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            // fazer pre-verificacao da password
            if (!IsValidPassword(password))
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (!IsValidEmail(email))
            {
                status = MembershipCreateStatus.InvalidEmail;
                return null;
            }

            // verificar se o utilizador ja existe
            MembershipUser membershipUser = GetUser(username, false);
            if (membershipUser != null)
            {
                status = MembershipCreateStatus.DuplicateUserName;
                return null;
            }
            string _user = GetUserNameByEmail(email);
            if (_user != null)
            { //verifica se email ja esta atribuido
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }
            else
            {
                //criar novo user e configurar
                User u = new User
                {
                    Username = username,
                    Password = password,
                    Email = email,
                    Type = "Regular User",
                    Disabled = false,
                    FirstName = firstName,
                    LastName = lastName,
                    Address = address,
                    Foto = photograph
                };
                db.Users.InsertOnSubmit(u);
                db.SubmitChanges();                         //submissão na base de daodos
                status = MembershipCreateStatus.Success;

                return membershipUser;
            }
        }

        //Não Original do provider : Validar email
        public bool IsValidEmail(string email)
        {
			Regex reg = new Regex(@"^(?("")("".+?""@)|(([0-9a-zA-Z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-zA-Z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,6}))$");
			return reg.IsMatch(email);					// TODO more checks

        }

        //Validar login
        public bool IsValidPassword(string password)
        {
            return (password.Length >= minRequiredPasswordLength);					// TODO more checks if needed
        }

        public override string ApplicationName
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        //mudar password
        public override bool ChangePassword(string username, string oldPwd, string newPwd)
        {
            if (!ValidateUser(username, oldPwd)) { return false; }
            if (!IsValidPassword(newPwd)) { return false; }

            User u = db.Users.Single(p => p.Username == username);
            u.Password = newPwd;
            db.SubmitChanges();
            return true;
        }

        //password parameters
        //password strenght
        public override string PasswordStrengthRegularExpression
        {
            get { return "*"; }
        }

        //nº minimo de caracteres apha numericos
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return 0; }
        }

        //tamanho da password
        public override int MinRequiredPasswordLength
        {
            get { return minRequiredPasswordLength; }
        }

        //configuração da password retrival
        public override bool EnablePasswordRetrieval
        {
            get { return false; }
        }

        // Metodos de pesquisa de users
        //getuser by email
        public override string GetUserNameByEmail(string email)
        {
            try
            {
                return db.Users.Single(p => p.Email == email).Username;
            }
            catch (InvalidOperationException)
            {
                //TODO TRATAR EXCEÇÂO : pra onde se reencaminha a informação de que estava vazio ou se não é preciso.
                return null;
            }
        }

        // configuração do Original do provider : procura user por username
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            // userIsOnline é ignorado, para já nao precisamos de usar
            try
            {
                return convertUserToMemb(db.Users.Single(p => p.Username == username));
            }
            catch (InvalidOperationException)
            {
                //TODO TRATAR EXCEÇÂO : pra onde se reencaminha a informação de que estava vazio ou se não é preciso.
                return null;
            }
        }


        // versão simplificada para o que precisamos para ja
        public User GetUser(string username)
        {
            // userIsOnline é ignorado, para já nao precisamos de usar
            try
            {
                return db.Users.Single(p => p.Username == username);
            }
            catch (InvalidOperationException)
            {
                //TODO TRATAR EXCEÇÂO : pra onde se reencaminha a informação de que estava vazio ou se não é preciso.
                return null;
            }
        }
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection list = new MembershipUserCollection();
            User u = db.Users.First(p => p.Email == emailToMatch);
            if (u != null)
                list.Add(convertUserToMemb(u));
            totalRecords = list.Count;
            if (list != null)                                                       //verificar se esta vazia
                return list;
            else
            {
                // Se se quiser tratar a lista vazia de alguma forma antes de passar null
                return null;
            }
        }

        public MembershipUser convertUserToMemb(User u)
        {
            return new MembershipUser("ShareBMembershipProvider", u.FirstName + " " + u.LastName, null, u.Email, "none", " ", true, false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
        }


        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection list = new MembershipUserCollection();
            User u = db.Users.First(p => p.Username == usernameToMatch);
            if (u != null)
                list.Add(convertUserToMemb(u));
            totalRecords = list.Count;
            return list;
        }

        public override int GetNumberOfUsersOnline()
        {
            throw new NotSupportedException();
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            MembershipUserCollection list = new MembershipUserCollection();
            totalRecords = db.Users.Count();
            int toSkip = (pageIndex - 1) * pageSize;
            int toTake = Math.Min(pageSize, totalRecords - toSkip);
            IEnumerable<User> users = db.Users;
            foreach (User u in users)
                list.Add(convertUserToMemb(u));
            return list;
        }

        

        #region Not suported

        //metodos de manipular users
        //activar um utilizador
        public override bool UnlockUser(string userName)
        {
            throw new NotSupportedException();
        }

        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion,
            string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            throw new NotSupportedException();
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new NotImplementedException();
        }


        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            throw new NotSupportedException();
        }

        public override void UpdateUser(MembershipUser user)
        {
            throw new NotImplementedException();
        }

        public override string ResetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }

        public override string GetPassword(string username, string answer)
        {
            throw new NotSupportedException();
        }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new NotSupportedException();
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { throw new NotImplementedException(); }
        }

        public override bool RequiresUniqueEmail
        {
            get { throw new NotImplementedException(); }
        }

        public override bool EnablePasswordReset
        {
            get { throw new NotImplementedException(); }
        }

        public override int PasswordAttemptWindow
        {
            get { throw new NotImplementedException(); }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { throw new NotImplementedException(); }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }


    // Provider for authorizations(roles)
    public class ShareBRoleProvider : RoleProvider
	{
		private static string[] roles = { "Regular User", "Moderator", "Admin" };

		private string connectionString;
        ShareBDataContext db;

		public override void Initialize(string name, NameValueCollection config)
		{
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}

			if (name == null || name.Length == 0)
			{
				name = "MyRoleProvider";
			}

			base.Initialize(name, config);

			ConnectionStringSettings connSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
			if (connSettings == null || String.IsNullOrEmpty(connSettings.ConnectionString))
				throw new ProviderException("The connection string is null or empty.");
			connectionString = connSettings.ConnectionString;
            db = new ShareBDataContext(connectionString);
		}

		public override string[] GetAllRoles()
		{
			return (string[])roles.Clone();                        // TODO pode ser estendido para ir a BD buscar a lista
		}

		public override bool RoleExists(string roleName)
		{
			return roles.Contains(roleName);
		}

		public override string[] GetRolesForUser(string username)
		{
			
            User u = db.Users.Single(p => p.Username == username);
			if (!RoleExists(u.Type))
				throw new ProviderException("The type of this user is null or invalid");
			return new string[] { u.Type };
		}

		public override bool IsUserInRole(string username, string roleName)
		{
			return GetRolesForUser(username).Contains(roleName);
		}

        #region Not implemented
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
		{
			throw new System.NotImplementedException();
		}

		public override void CreateRole(string roleName)
		{
			throw new System.NotImplementedException();
		}

		public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
		{
			throw new System.NotImplementedException();
		}

		public override string[] FindUsersInRole(string roleName, string usernameToMatch)
		{
			throw new System.NotImplementedException();
		}

		public override string[] GetUsersInRole(string roleName)
		{
			throw new System.NotImplementedException();
		}

		public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
		{
			throw new System.NotImplementedException();
		}

		public override string ApplicationName
		{
			get
			{
				throw new System.NotImplementedException();
			}
			set
			{
				throw new System.NotImplementedException();
			}
        }
        #endregion
    }
}